package com.jintian.smart.kernel.flowable.component.action.base;

import com.alibaba.fastjson.JSON;
import com.jintian.smart.kernel.common.spring.BeanSelfAware;
import com.jintian.smart.kernel.common.util.StringUtil;
import com.jintian.smart.kernel.core.annotation.flow.TaskOperate;
import com.jintian.smart.kernel.core.dto.OrgUnit;
import com.jintian.smart.kernel.core.flow.FlowAction;
import com.jintian.smart.kernel.core.flow.ITaskAction;
import com.jintian.smart.kernel.core.flow.ProcessControl;
import com.jintian.smart.kernel.flowable.context.DefaultProcessContext;
import com.jintian.smart.kernel.flowable.domain.entity.FlowActionLog;
import com.jintian.smart.kernel.flowable.engine.BaseProcessService;
import com.jintian.smart.kernel.flowable.engine.DefaultFlowRuntime;
import com.jintian.smart.kernel.flowable.mapper.IFlowActionLogMapper;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.ExtensionAttribute;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public abstract class AbstractTaskAction extends BaseProcessService implements ITaskAction<List<TaskInfo>>, BeanSelfAware {
    @Autowired
    protected IFlowActionLogMapper flowActionLogMapper;

    @Override
    @Transactional
    @TaskOperate
    public List<TaskInfo> execute(String taskId, ProcessControl processControl) throws Exception {
        TaskEntity task = this.loadTask(taskId);
        if (processControl == null) {
            ProcessInstance pi = this.runtimeService().createProcessInstanceQuery().includeProcessVariables().processInstanceId(task.getProcessInstanceId()).singleResult();
            processControl = new ProcessControl();
            processControl.setProcess(pi.getProcessDefinitionKey());
            processControl.setBusinessKey(pi.getBusinessKey());
        }
        FlowActionLog log = new FlowActionLog();
        log.setTaskId(taskId);
        log.setBusinessKey(processControl.getBusinessKey());
        log.setExecuteTime(LocalDateTime.now());
        log.setOperatorId(FlowAction.getFlowOwner().getId());
        log.setOperatorName(FlowAction.getFlowOwner().getName());
        log.setProcessControl(JSON.toJSONString(processControl));
        log.setActionName(this.getAction().name());
        if (StringUtil.isBlank(log.getBusinessKey())) {
            ProcessInstance pi = this.runtimeService().createProcessInstanceQuery().includeProcessVariables().processInstanceId(task.getProcessInstanceId()).singleResult();
            log.setBusinessKey(pi.getBusinessKey());
        }
        flowActionLogMapper.insert(log);
        this.checkTask(task, FlowAction.getFlowOwner(), this.mustCheck(processControl));
        return (List<TaskInfo>) (proxy != null ? proxy : this).innerExecute(task, processControl);
    }

    private AbstractTaskAction proxy;

    @Override
    public void setSelf(Object proxyBean) {
        this.proxy = this;
    }

    protected abstract List<TaskInfo> innerExecute(TaskEntity task, ProcessControl processControl) throws Exception;

    protected TaskCheckEnum[] mustCheck(ProcessControl processControl) {
        if (processControl.isNoCheckOwner()) {
            return new TaskCheckEnum[]{TaskCheckEnum.Active};
        }
        return BaseProcessService.DEFAULT_TASK_CHECK;
    }

    protected TaskEntity createSubTask(TaskEntity parent) {
        TaskEntity task = (TaskEntity) taskService().createTaskBuilder()
                .parentTaskId(parent.getId())
                .taskDefinitionId(parent.getTaskDefinitionId())
                .taskDefinitionKey(parent.getTaskDefinitionKey())
                .category(parent.getCategory())
                .formKey(parent.getFormKey())
                .name(parent.getName())
                .priority(parent.getPriority())
                .tenantId(parent.getTenantId())
                .create();
        task.setExecutionId(parent.getExecutionId());
        task.setProcessDefinitionId(parent.getProcessDefinitionId());
        task.setProcessInstanceId(parent.getProcessInstanceId());
        return task;
    }

    abstract public List<? extends FlowNode> queryTaskNextActivityNode(Task task);

    @Override
    public ProcessControl query(String taskId, Map<String, Object> transientVariables) throws Exception {
        Map<String, Object> context = new HashMap<>();
        if (transientVariables != null) {
            context.putAll(transientVariables);
        }
        Task task = this.loadTask(taskId);
        this.buildFlowRuntimeContext(task, context);
        ProcessInstance pi = (ProcessInstance) context.get(DefaultFlowRuntime.CONTEXT_PROCESSINSTANCE);
        ProcessControl processControl = new ProcessControl();
        processControl.setProcess(pi.getProcessDefinitionKey());
        processControl.setBusinessKey(pi.getBusinessKey());

        List<? extends FlowNode> nextTaskNode = this.queryTaskNextActivityNode(task);
        if (nextTaskNode.size() == 0) {
            return processControl;
        } else {
            String currentActivity = task.getTaskDefinitionKey();
            String bizClass = (String) pi.getProcessVariables().get(FlowAction.VARNAME_START_BIZCLASS);
            String modelName = (String) pi.getProcessVariables().get(FlowAction.VARNAME_START_MODELNAME);
            DefaultFlowRuntime flowRuntime = this.createFlowRuntime(bizClass, modelName, pi.getProcessDefinitionKey(), pi.getProcessDefinitionVersion(), context);

            try (DefaultProcessContext processContext = (DefaultProcessContext) this.processContextProvider.createInstance(pi.getProcessDefinitionKey(), task.getTaskDefinitionKey(), this.getAction(), pi.getProcessInstanceId(), task.getId(), pi.getBusinessKey(), processControl);) {
                processContext.setFlowRuntime(flowRuntime);
                processControl.setSilence(flowRuntime.getFlow() != null && Boolean.TRUE.equals(flowRuntime.getFlow().getSilence()));
                // 1. 设置流程环境,用于表达式计算
                flowRuntime.getContext().put("context", processContext);
                // 2. 应用变量到上下文环境中，开始计算下一环节。(不建议业务规则中使用变量，直接使用表、字段容易理解)
                flowRuntime.applyProcessVariablesToContext();
                // 3.检查业务规则
                List<Object> checkResult = flowRuntime.checkRule(currentActivity, this.getAction());
                if (checkResult.size() > 0) {
                    processControl.setRuleResult(checkResult);
                    if (flowRuntime.existStopRule(checkResult)) {
                        processControl.setStop(true);
                        return processControl;
                    }
                }
                nextTaskNode.stream().filter((target) -> {
                    if (FlowAction.transform.equals(getAction()) && target.getId().equals(currentActivity)) {
                        // 如果转发(环节内流转)
                        return true;
                    }
                    List<ExtensionAttribute> attrsList = target.getAttributes().remove("conditionExpression");
                    if (attrsList == null) {
                        // 计算执行表达式、流转表达式
                        return flowRuntime.canFlowTo(currentActivity, target.getId());
                    } else {
                        // 计算连线的生效条件
                        for (ExtensionAttribute item : attrsList) {
                            String conditionExpression = item.getValue();
                            boolean canFlowTo = flowRuntime.hasTrueCondition("环节(" + target.getId() + ")", conditionExpression);
                            if (log.isDebugEnabled()) {
                                log.debug("flowTo {} condition {} is {}", target.getId(), conditionExpression, canFlowTo);
                            }
                            if (!canFlowTo) {
                                return false;
                            }
                        }
                        return true;
                    }
                }).forEach((target) -> {
                    ProcessControl.ProcessControlItem flowTo = new ProcessControl.ProcessControlItem();
                    flowTo.setActivity(target.getId());
                    flowTo.setEnd(target instanceof EndEvent);
                    flowTo.setActivityLabel(flowTo.isEnd() ? "办结" : target.getName());
                    if (FlowAction.complete.equals(getAction()) && target instanceof UserTask) {
                        UserTask userTask = (UserTask) target;
                        // 计算执行者
                        for (String userId : userTask.getCandidateUsers()) {
                            OrgUnit executor = operatorQuery.query("user", userId, false);
                            if (executor != null) {
                                flowTo.getExecutorRange().add(executor);
                            } else {
                                log.warn("流程" + pi.getProcessDefinitionKey() + "环节" + target.getId() + ":\n不存在id为" + userId + "候选人");
                            }
                        }
                        for (String groupId : userTask.getCandidateGroups()) {
                            String[] args = groupId.split(":");
                            String kind = null;
                            String code = null;
                            if (args.length == 1) {
                                kind = "role";
                                code = args[0];
                            } else {
                                kind = args[0];
                                code = args[1];
                            }
                            OrgUnit executor = operatorQuery.query(kind, code, false);

                            if (executor != null) {
                                flowTo.getExecutorRange().add(executor);
                            } else {
                                log.warn("流程" + pi.getProcessDefinitionKey() + "环节" + target.getId() + ":\n不存在code为" + code + "候选" + kind);
                            }
                        }
                    }
                    if (FlowAction.complete.equals(getAction())) {
                        flowRuntime.calcExecutorRules(flowTo);
                    } else if (FlowAction.transform.equals(getAction())) {
                        flowRuntime.calcTransformRules(flowTo);
                    }

                    processControl.getFlowTos().add(flowTo);
                    if (!flowTo.isEnd() && flowTo.getExecutorRange().size() == 0 && flowTo.getExecutors().size() == 0) {
                        List<OrgUnit> grantList = this.flowLoader.getGrantOrgUnit(pi.getProcessDefinitionKey(), pi.getProcessDefinitionVersion(), flowTo.getActivity());
                        if (grantList.size() == 0) {
                            processControl.setSilence(false);
                            processControl.setStop(false);
                            processControl.addRuleMsg(flowRuntime.createRuleMessage("", flowTo.getActivityLabel() + "未设置执行者，您将成为任务的默认执行人", false));
                        } else {
                            flowTo.getExecutorRange().addAll(grantList);
                        }
                    }
                });
            }
        }
        return processControl;
    }
}
